<?php

namespace org\copperforest\google;
/**
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 * 
 * TENGO QUE PODER SUBIR POR PARTES Y SINCRONIZAR LA SUBIDA A MI PLATAFORMA CON LA DE GOOGLE
 */
//TIPO DE DATOS DE UNA entry <category scheme="http://schemas.google.com/g/2005#kind" term=":

/*
  document - http://schemas.google.com/docs/2007#document
  drawing - http://schemas.google.com/docs/2007#drawing
  file - http://schemas.google.com/docs/2007#file
  folder - http://schemas.google.com/docs/2007#folder
  pdf - http://schemas.google.com/docs/2007#pdf
  presentation
  spreadsheet
 */
// http://schemas.google.com/docs/2007#metadata
// http://schemas.google.com/docs/2007#change
// http://schemas.google.com/acl/2007#accessRule
// http://schemas.google.com/docs/2007#archive //zip

/**
 * Description of GoogleAPI
 *
 * @author agama
 */
class GShareAPI
{

const CRLF = "\r\n";
const SERVICE_ANALYTICS = 'analytics';
const SERVICE_APPS = 'apps'; //(Domain Information & Management)
const SERVICE_SITES = 'jotspot';
const SERVICE_BLOGGER = 'blogger';
const SERVICE_BOOK_SEARCH = 'print';
const SERVICE_CALENDAR = 'cl';
const SERVICE_CODE_SEARCH = 'codesearch';
const SERVICE_CONTACTS = 'cp';
const SERVICE_SHOPPING = 'structuredcontent';
const SERVICE_DOCS = 'writely';
const SERVICE_DOCS_FULL = 'writely_full';
const SERVICE_FINANCE = 'finance';
const SERVICE_GMAIL = 'mail';
const SERVICE_HEALTH = 'health';
const SERVICE_HEALTH_H9 = 'weaver';
const SERVICE_PICASA = 'lh2';
const SERVICE_SIDEWIKI = 'annotateweb';
const SERVICE_SPREADSHEETS = 'wise';
const SERVICE_WEBMASTER = 'sitemaps';
const SERVICE_YOUTUBE = 'youtube';
const SERVICE_MAPS = 'maps'; //?

???

public static $instances = array( );
private static $scopes = array(
self::SERVICE_ANALYTICS => 'https://www.google.com/analytics/feeds/',
 self::SERVICE_SITES => 'https://sites.google.com/feeds/',
 self::SERVICE_BLOGGER => 'http://www.blogger.com/feeds/',
 self::SERVICE_BOOK_SEARCH => 'http://www.google.com/books/feeds/',
 self::SERVICE_CALENDAR => 'https://www.google.com/calendar/feeds/',
 self::SERVICE_CONTACTS => 'https://www.google.com/m8/feeds/',
 self::SERVICE_SHOPPING => 'https://www.googleapis.com/auth/structuredcontent',
 self::SERVICE_DOCS => 'https://docs.google.com/feeds/',
 self::SERVICE_DOCS_FULL => 'https://docs.google.com/feeds/%20https://spreadsheets.google.com/feeds/%20https://docs.googleusercontent.com',
 self::SERVICE_FINANCE => 'http://finance.google.com/finance/feeds/',
 self::SERVICE_GMAIL => 'https://mail.google.com/mail/feed/atom/',
 self::SERVICE_HEALTH => 'https://www.google.com/health/feeds/',
 self::SERVICE_HEALTH_H9 => 'https://www.google.com/h9/feeds/',
 self::SERVICE_MAPS => 'http://maps.google.com/maps/feeds/',
 self::SERVICE_PICASA => 'http://picasaweb.google.com/data/',
 self::SERVICE_SIDEWIKI => 'http://www.google.com/sidewiki/feeds/',
 self::SERVICE_SPREADSHEETS => 'https://spreadsheets.google.com/feeds/',
 self::SERVICE_WEBMASTER => 'http://www.google.com/webmasters/tools/feeds/',
 self::SERVICE_YOUTUBE => 'http://gdata.youtube.com'
);
//OJO LOS FICHEROS TIENE DOMINIOS DEL ESTILO:doc-0c-04-docs.googleusercontent.com

private static $url2service = array(
'www.google.com' => array( ), //self::SERVICE_SIDEWIKI,self::SERVICE_WEBMASTER
'ssl://www.google.com' => array( ), //self::SERVICE_ANALYTICS,self::SERVICE_BOOK_SEARCH,self::SERVICE_CALENDAR,self::SERVICE_CONTACTS,self::SERVICE_HEALTH,self::SERVICE_HEALTH_H9
'ssl://sites.google.com' => array( '' => self::SERVICE_SITES ),
 'www.blogger.com' => array( '' => self::SERVICE_BLOGGER ),
 'ssl://www.googleapis.com' => array( ), //self::SERVICE_SHOPPING
'ssl://docs.google.com' => array( '' => self::SERVICE_DOCS ), //self::SERVICE_DOCS_FULL
'finance.google.com' => array( '' => self::SERVICE_FINANCE ),
 'ssl://mail.google.com' => array( '' => self::SERVICE_GMAIL ),
 'maps.google.com' => array( '' => self::SERVICE_MAPS ),
 'picasaweb.google.com' => array( '' => self::SERVICE_PICASA ),
 'ssl://spreadsheets.google.com' => array( '' => self::SERVICE_SPREADSHEETS ),
 'gdata.youtube.com' => array( '' => self::SERVICE_YOUTUBE )
);
private static $type2service = array(
'document' => self::SERVICE_DOCS,
 'drawing' => self::SERVICE_DOCS,
 'file' => self::SERVICE_DOCS_FULL,
 'folder' => self::SERVICE_DOCS,
 'pdf' => self::SERVICE_DOCS,
 'spreadsheet' => self::SERVICE_SPREADSHEETS,
 'presentation' => self::SERVICE_DOCS_FULL
);
//la cedna vacia es el valor por defecto
//http://code.google.com/intl/es/apis/documents/faq.html#WhatKindOfFilesCanIUpload
private static $ext2mime = array(
'csv' => 'text/csv',
 'tsv' => 'text/tab-separated-values',
 'tab' => 'text/tab-separated-values',
 'htm' => 'text/html',
 'html' => 'text/html',
 'doc' => 'application/msword',
 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
 'ods' => 'application/x-vnd.oasis.opendocument.spreadsheet',
 'odt' => 'application/vnd.oasis.opendocument.text',
 'rtf' => 'application/rtf',
 'sxw' => 'application/vnd.sun.xml.writer',
 'txt' => 'text/plain',
 'xls' => 'application/vnd.ms-excel',
 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
 'pdf' => 'application/pdf',
 'ppt' => 'application/vnd.ms-powerpoint',
 'pps' => 'application/vnd.ms-powerpoint',
 'gif' => 'image/png',
 'jpg' => 'image/png',
 'jpeg' => 'image/png',
 'png' => 'image/png',
 '' => 'application/octet-stream'
);
//http://code.google.com/intl/es/apis/documents/docs/2.0/developers_guide_protocol.html#DownloadingDocsAndPresentations
private static $mime2export = array(
'text/csv' => 'csv',
 'text/tab-separated-values' => 'tsv',
 'text/html' => 'html',
 'application/msword' => 'doc',
 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'doc',
 'application/x-vnd.oasis.opendocument.spreadsheet' => 'ods',
 'application/vnd.oasis.opendocument.text' => 'odt',
 'application/rtf' => 'rtf',
 'application/vnd.sun.xml.writer' => 'odt',
 'text/plain' => 'txt',
 'application/vnd.ms-excel' => 'xls',
 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'xls',
 'application/pdf' => 'pdf',
 'application/vnd.ms-powerpoint' => 'ppt',
 'image/jpeg' => 'png',
 'image/gif' => 'png',
 'image/png' => 'png'
);
private static $mime2type = array(
'text/csv' => 'spreadsheet',
 'text/tab-separated-values' => 'spreadsheet',
 'text/html' => 'document',
 'application/msword' => 'document',
 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'document',
 'application/x-vnd.oasis.opendocument.spreadsheet' => 'document',
 'application/vnd.oasis.opendocument.text' => 'document',
 'application/rtf' => 'document',
 'application/vnd.sun.xml.writer' => 'document',
 'text/plain' => 'document',
 'application/vnd.ms-excel' => 'spreadsheet',
 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'spreadsheet',
 'application/pdf' => 'document',
 'application/vnd.ms-powerpoint' => 'presentation',
 'image/jpeg' => 'drawing',
 'image/gif' => 'drawing',
 'image/png' => 'drawing'
);

//tendría que hacer un array mapeado de mime type a formato de exportacion
// un array inveso de extension a mime-type???
//Portable Contacts API 	http://www-opensocial.googleusercontent.com/api/people

const PERMISSION_OWNER = 'owner';
const PERMISSION_WRITER = 'writer';
const PERMISSION_READER = 'reader';
const CLIENT_ID = '76748848756.apps.googleusercontent.com';
const CLIENT_SECRET = 'yfZGM3sxAA-SWbZ2dgREDRWQ';
const CLIENT_REDIRECT_URI = 'http://localhost/oauth.php';

private $mysqli;
private $shared_account;
private $shared_password;
private $resumable_create_media;
private $post_link;
private $created = false;
private $db;
private $auth_token = array( ); //depende del servicio
private $token_expires = array( );
private $basic_headers = array( );

//debería pasarle un directorio temporal!!!!!!
function __construct( $shared_account, MySQLi $mysqli, $db = null )
{
$this->mysqli = $mysqli;
$this->shared_account = $shared_account;
$this->db = $db;


if ( $this->db != null ) {

$result = $this->mysqli->query( "SELECT DATABASE()" );
list($db, ) = $result->fetch_row();

if ( $db != $this->db ) $this->mysqli->select_db( $this->db );
}

$result = $this->mysqli->query( 'SELECT password, resumable_create_media, post FROM google_accounts WHERE account=\'' . $this->shared_account . '\'' );


if ( $result->num_rows > 0 ) {

$this->created = true;

list( $pass_enc, $this->resumable_create_media, $this->post_link ) = $result->fetch_row();
$result->free();

//$iv_size=mcrypt_get_block_size(MCRYPT_3DES,MCRYPT_MODE_ECB);
$iv_size = 8;
$iv = mcrypt_create_iv( $iv_size, MCRYPT_RAND );

$this->shared_password = @mcrypt_decrypt( MCRYPT_3DES,
 self::CLIENT_SECRET, $pass_enc, MCRYPT_MODE_ECB, $iv );
}


if ( $this->db != null && $db != $this->db )
$this->mysqli->select_db( $db );
}

//INICIALIZO SOCKET CON EL HOST
//OBTENGO UN AUTH TOKEN: LE DOY PERMISOS A LA APLICACION PARA QUE PUEDA OPERAR CON LOS DATOS DE LA CUENTA EN ESE SCOPE
//Siempre hacemos el login en el servicio antes de cualquier operacion
//EN luegar de un servicio podría pasar una URL y que me devuelva el socket y el path?
private function init( $url )
{

$link = parse_url( $url );
$host = (($link[ 'scheme' ] == 'https') ? 'ssl://' : '') . $link[ 'host' ];
$resource = $link[ 'path' ];


if (!isset( self::$url2service[ $host ] ) ) return false;

$service = null;

if (!isset( self::$url2service[ $host ][ $resource ] ) ) {

if (!isset( self::$url2service[ $host ][ '' ] ) ) return false;

$service = self::$url2service[ $$host ][ $resource ];
}
else $service = self::$url2service[ $host ][ $resource ];

$t = $_SERVER[ 'REQUEST_TIME' ];
if ( empty( $this->socket[ $host ] ) ) //no debiera utilizar los sockets por servicio sino por host
$this->socket[ $host ] = fsockopen( $host,
 ( strpos( $host, 'ssl://' ) == 0 ) ? 443 : 80, $errno,
 $errstr, 30 );


$resource = $resource . ( empty( $link[ 'query' ] ) ? '' : '?' . $link[ 'query' ] );

if ( isset( $this->auth_token[ $service ] ) && $this->token_expires[ $service ] > $t + 60 )
return array( $this->socket[ $host ], $resource, $this->basic_headers[ $service ] );


if ( $this->db != null ) {

$result = $this->mysqli->query( 'SELECT DATABASE()' );
list($db, ) = $result->fetch_row();
$result->free();

if ( $db != $this->db ) $this->mysqli->select_db( $this->db );
}

$result = $this->mysqli->query( 'SELECT token, token_type,  UNIX_TIMESTAMP(expires) FROM google_accounts_tokens WHERE account=\'' . $this->shared_account . '\' AND service=\'' . $service . '\'' );

$row_exists = false;


if ( $result->num_rows > 0 ) {
$row_exists = true;

list($token, $token_type, $expires) = $result->fetch_row();
$result->free();

$expires = ( int ) $expires;
if ( $expires > $t + 60 ) { //tenemos un token que es válido durante al menos 60 segundos más, tiempo más que suficiente
$this->auth_token[ $service ] = 'Authorization: ' . $token_type . ' ' . $token;
$this->token_expires[ $service ] = $expires;

$this->basic_headers[ $service ] = 'GData-Version: 3.0' . self::CRLF;
$this->basic_headers[ $service ] .= $this->auth_token[ $service ] . self::CRLF;
$this->basic_headers[ $service ] .= 'Host: ' . $host . self::CRLF;
$this->basic_headers[ $service ] .= 'Connection: Keep-Alive' . self::CRLF;

if ( $this->db != null && $db != $this->db )
$this->mysqli->select_db( $db );

return array( $this->socket[ $host ], $resource, $this->basic_headers[ $service ] );
}
}
else $row_exists = false;


$curl = curl_init();
$uniqid = uniqid();

$ini_options = array(
CURLOPT_AUTOREFERER => true,
 CURLOPT_BINARYTRANSFER => true,
 //   CURLOPT_CERTINFO=>false,
CURLOPT_COOKIESESSION => false,
 CURLOPT_DNS_USE_GLOBAL_CACHE => false,
 CURLOPT_FAILONERROR => false,
 CURLOPT_FILETIME => false,
 CURLOPT_FOLLOWLOCATION => true,
 CURLOPT_FORBID_REUSE => false,
 CURLOPT_FRESH_CONNECT => false,
 CURLOPT_HEADER => false,
 CURLINFO_HEADER_OUT => false,
 CURLOPT_HTTPGET => true,
 CURLOPT_NOPROGRESS => true,
 CURLOPT_RETURNTRANSFER => true,
 CURLOPT_SSL_VERIFYPEER => false,
 CURLOPT_VERBOSE => true,
 //CURLOPT_HTTP_VERSION=>CURLOPT_HTTP_VERSION_1_1,
CURLOPT_PORT => 443,
 CURLOPT_SSL_VERIFYHOST => true,
 CURLOPT_SSLVERSION => 3,
 CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 5.1; rv:9.0.1) Gecko/20100101 Firefox/9.0.1',
 CURLOPT_COOKIEJAR => TEMP_PATH . 'cookie' . $uniqid . '.txt', //OJO NO PUEDO USAR EL MISMO ARCHIVO PARA TODAS LAS CONEXIONES, etndría que ser una por empresa
CURLOPT_COOKIEFILE => TEMP_PATH . 'cookie' . $uniqid . '.txt'
);


curl_setopt_array( $curl, $ini_options );

$data = array(
'scope' => self::$scopes[ $service ], //https://docs.google.com/feeds/ https://docs.googleusercontent.com/ https://spreadsheets.google.com/feeds/
'redirect_uri' => self::CLIENT_REDIRECT_URI,
 'response_type' => 'code',
 'client_id' => self::CLIENT_ID
);

$data_encoded = array( );
foreach ( $data as $k => $v )
$data_encoded[ ] = urlencode( $k ) . '=' . urlencode( $v );

curl_setopt( $curl, CURLOPT_URL,
 'https://accounts.google.com/o/oauth2/auth?' . implode( '&',
 $data_encoded ) );
$contenido = curl_exec( $curl );

//Generamos siguiente llamada rellenando el formulario con los datos de la cuenta compartida
$pos_ini = strpos( $contenido, '<form' );
$pos_fin = strpos( $contenido, '</form>', $pos_ini );
$form_content = substr( $contenido, $pos_ini, $pos_fin - $pos_ini + 7 );

//eliminamos otras etiquetas excepto form e input
$form_content = strip_tags( $form_content, '<form><input>' );
$form_content = strrev( preg_replace( '/>\/?([^>]*tupni<)/', '>/ ${1}',
 strrev( $form_content ) ) );

$form = simplexml_load_string( strtr( $form_content, array( '&nbsp;' => '' ) ) );
unset( $contenido, $form_content );

$fields = array( );
foreach ( $form->input as $i )
if ( $i[ 'name' ] != 'Email' && $i[ 'name' ] != 'Passwd' )
$fields[ ] = urlencode( $i[ 'name' ] ) . '=' . urlencode( $i[ 'value' ] );

$fields[ ] = urlencode( 'Email' ) . '=' . urlencode( $this->shared_account );
$fields[ ] = urlencode( 'Passwd' ) . '=' . urlencode( $this->shared_password );

unset( $contenido, $form_content );

curl_setopt( $curl, CURLOPT_URL, $form[ 'action' ] );
curl_setopt( $curl, CURLOPT_POST, true );
curl_setopt( $curl, CURLOPT_POSTFIELDS, implode( '&', $fields ) );

$contenido = curl_exec( $curl );


//Generamos la siguiente llamada rellenando el formulario autorizando a la aplicación en el scope dado de la cuenta compartida
$pos_ini = strpos( $contenido, '<form' );
$pos_fin = strpos( $contenido, '</form>', $pos_ini );
$form_content = substr( $contenido, $pos_ini, $pos_fin - $pos_ini + 7 );

//eliminamos otras etiquetas excepto form e input
$form_content = strip_tags( $form_content, '<form><input>' );
$form_content = strrev( preg_replace( '/>\/?([^>]*tupni<)/', '>/ ${1}',
 strrev( $form_content ) ) );

$form = simplexml_load_string( strtr( $form_content, array( '&nbsp;' => '' ) ) );
unset( $contenido, $form_content );


$fields = array( );
foreach ( $form->input as $i )
if ( $i[ 'name' ] != 'deny' )
$fields[ ] = urlencode( $i[ 'name' ] ) . '=' . urlencode( $i[ 'value' ] );

unset( $contenido, $form_content );

curl_setopt( $curl, CURLOPT_URL, $form[ 'action' ] );
curl_setopt( $curl, CURLOPT_POST, true );
curl_setopt( $curl, CURLOPT_POSTFIELDS, implode( '&', $fields ) );
curl_setopt( $curl, CURLOPT_FOLLOWLOCATION, false );
curl_setopt( $curl, CURLOPT_HEADER, true ); //obtengo la cabecera de respuesta

$contenido = curl_exec( $curl ); //DE aqui tengo que extraer el code;
//obtenemos el codigo para obtener el token
$contenido = substr( $contenido, 0, strpos( $contenido, "\r\n\r\n" ) ); //solo queremos la cabecera

$coincidencias = array( );
preg_match( "/Location\s*:[^\?]*\?code=([^\r]*)/", $contenido,
 $coincidencias );
$code = urldecode( $coincidencias[ 1 ] );
unset( $contenido );


//Generamos la llamada para obtener el token a partir del code
$data = array(
'code' => $code,
 'redirect_uri' => self::CLIENT_REDIRECT_URI,
 'client_id' => self::CLIENT_ID,
 'client_secret' => self::CLIENT_SECRET,
 'grant_type' => 'authorization_code'
);

$fields = array( );
foreach ( $data as $k => $v ) $fields[ ] = urlencode( $k ) . '=' . urlencode( $v );

curl_setopt_array( $curl, $ini_options );
curl_setopt( $curl, CURLOPT_URL,
 'https://accounts.google.com/o/oauth2/token' );
curl_setopt( $curl, CURLOPT_POST, true );
curl_setopt( $curl, CURLOPT_POSTFIELDS, implode( '&', $fields ) );

$contenido = curl_exec( $curl );


//por ultimo obtenemos los datos de la respuesta
$values = array( );
if ( function_exists( 'json_decode' ) )
$values = json_decode( $contenido, true );

else {

//voy a simplificar esto para que sea más generalista
$coincidencias = array( );
preg_match_all( '/"([^"]*)"\s*:\s*([^,}]*)/', $contenido,
 $coincidencias );

$values = array( );
$num = count( $coincidencias[ 1 ] );
for ( $i = 0;
$i < $num;
$i++) {
$coin = array( );

if ( preg_match( '/"([^"]*)"/', $coincidencias[ 2 ][ $i ], $coin ) )
$values[ $coincidencias[ 1 ][ $i ] ] = $coin[ 1 ];

else if ( preg_match( '/(\d*)/', $coincidencias[ 2 ][ $i ],
 $coin ) )
$values[ $coincidencias[ 1 ][ $i ] ] = ( int ) $coin[ 1 ];
}
}


$this->auth_token[ $service ] = 'Authorization: ' . $token_type . ' ' . $token;
$this->token_expires[ $service ] = $expires;

$this->basic_headers[ $service ] = 'GData-Version: 3.0' . self::CRLF;
$this->basic_headers[ $service ] .= $this->auth_token[ $service ] . self::CRLF;
$this->basic_headers[ $service ] .= 'Host: ' . $host . self::CRLF;
$this->basic_headers[ $service ] .= 'Connection: Keep-Alive' . self::CRLF;


if ( $row_exists )
$this->mysqli->query( 'UPDATE google_accounts_tokens SET token=\'' . $values[ 'access_token' ] . '\', token_type=\'' . $values[ 'token_type' ] . '\', expires=FROM_UNIXTIME(' . ($t + $values[ 'expires_in' ] - 60) . ') WHERE account=\'' . $this->shared_account . '\' AND service=\'' . $service . '\'' );
else
$this->mysqli->query( 'INSERT google_accounts_tokens (token,token_type,account,service,expires) VALUES ( \'' . $values[ 'access_token' ] . '\', \'' . $values[ 'token_type' ] . '\', \'' . $this->shared_account . '\', \'' . $service . '\', FROM_UNIXTIME(' . (time() + $values[ 'expires_in' ] - 60) . ') )' );

unlink( $ini_options[ CURLOPT_COOKIEJAR ] );

if ( $this->db != null && $db != $this->db )
$this->mysqli->select_db( $db );

return array( $this->socket[ $host ], $resource, $this->basic_headers[ $service ] );
}

function setPassword( $password )
{

//$iv_size=@mcrypt_get_block_size(MCRYPT_3DES,MCRYPT_MODE_ECB);
$iv_size = 8;
$iv = mcrypt_create_iv( $iv_size, MCRYPT_RAND );


$pass_enc = @mcrypt_encrypt( MCRYPT_3DES, self::CLIENT_SECRET,
 $password, MCRYPT_MODE_ECB, $iv );

if ( $this->db != null ) {

$result = $this->mysqli->query( "SELECT DATABASE()" );
list($db, ) = $result->fetch_row();
$result->free();

if ( $db != $this->db ) $this->mysqli->select_db( $this->db );
}

$this->shared_password = $password;

if (!$this->created ) {

list( $socket, $resource, $headers ) = $this->init( 'https://docs.google.com/feeds/default/private/full?max-results=1' );

$req = 'GET ' . $resource . ' HTTP/1.1' . self::CRLF;
$req .= $headers . self::CRLF;

fwrite( $socket, $req );

list( $code, $headers, $content ) = self::getHTTPResponse( $socket );

if (!empty( $content ) ) {

$xml = simplexml_load_string( $content );

foreach ( $xml->link as $l ) {
$rel = ( string ) $l[ 'rel' ];

if ( $rel == 'http://schemas.google.com/g/2005#resumable-create-media' )
$this->resumable_create_media = $l[ 'href' ];

else if ( $rel == 'http://schemas.google.com/docs/2007#alt-post' )
$alt_post_link = $l[ 'href' ];

else if ( $rel == 'http://schemas.google.com/g/2005#feed' ) //el propio feed?
$feed_link = $l[ 'href' ];

else if ( $rel == 'http://schemas.google.com/g/2005#post' ) //edital el feed
$this->post_link = $l[ 'href' ];

else if ( $rel == 'http://schemas.google.com/g/2005#batch' ) //batch al feed
$batch_link = $l[ 'href' ];
}


$this->mysqli->query( 'INSERT INTO google_accounts (account,password,resumable_create_media,alt_post,feed,post,batch) VALUES (\'' . $this->shared_account . '\',0x' . bin2hex( $pass_enc ) . ',0x' . bin2hex( $this->resumable_create_media ) . ',0x' . bin2hex( $alt_post_link ) . ',0x' . bin2hex( $feed_link ) . ',0x' . bin2hex( $this->post_link ) . ',0x' . bin2hex( $batch_link ) . ')' );
//Aqui debería obtener el 
$this->created = true;
}
else {

if ( $code == '401' || $code == '403' )
$this->mysqli->query( 'UPDATE google_accounts_tokens SET status=\'' . self::TOKEN_EXPIRED . '\' WHERE account=\'' . $this->shared_account . '\' AND service=\'' . self::SERVICE_DOCS . '\'' );

if ( $this->db != null && $db != $this->db )
$this->mysqli->select_db( $db );

return false;
}
}
else
$this->mysqli->query( 'UPDATE google_accounts SET password=0x' . bin2hex( $pass_enc ) . ' WHERE account=\'' . $this->shared_account . '\'' );



if ( $this->db != null && $db != $this->db )
$this->mysqli->select_db( $db );

return true;
}

function isUploaded( $local_file )
{
$local_file = realpath( $local_file );

$data = false;

if ( $this->db != null ) {

$result = $this->mysqli->query( 'SELECT DATABASE()' );
list($db, ) = $result->fetch_row();

if ( $db != $this->db ) $this->mysqli->select_db( $this->db );
}

$result = $this->mysqli->query( 'SELECT object_id, view_link, edit_link, export_link FROM google_uploaded_objects WHERE local_file=0x' . bin2hex( $local_file ) );
//PORQUE DA ERRROR AQUI DE VEZ EN CUANDO????????????


if ( $result->num_rows > 0 ) $data = $result->fetch_assoc();

if ( $this->db != null && $db != $this->db )
$this->mysqli->select_db( $db );

return $data;
}

function wasModified( $object_id )
{

if ( $this->db != null ) {

$result = $this->mysqli->query( 'SELECT DATABASE()' );
list($db, ) = $result->fetch_row();

if ( $db != $this->db ) $this->mysqli->select_db( $this->db );
}

$result = $this->mysqli->query( 'SELECT etag, UNIX_TIMESTAMP(local_updated) FROM google_uploaded_objects WHERE object_id=0x' . bin2hex( $object_id ) );

if ( $result->num_rows < 1 ) {

if ( $this->db != null && $db != $this->db )
$this->mysqli->select_db( $db );

return false;
}

list($etag, $local_updated) = $result->fetch_row();

if ( $this->db != null && $db != $this->db )
$this->mysqli->select_db( $db );


list( $socket, $resource, $headers ) = $this->init( 'https://docs.google.com/feeds/default/private/full/' . $object_id );

$req = 'GET ' . $resource . ' HTTP/1.1' . self::CRLF;
$req .= 'If-None-Match: ' . $etag . self::CRLF;
$req .= $headers . self::CRLF;

fwrite( $socket, $req );

list( $code, $headers, $content ) = self::getHTTPResponse( $socket );

if (!empty( $content ) ) {
$xml = simplexml_load_string( $content );

//$xml['etag'];
$google_updated = strtotime( ( string ) $xml->updated ); //$xml->edited;
//esto podría devolver
if ( $google_updated > $local_updated )
return array( $xml[ 'etag' ], $google_updated );
else return false;
}
else if ( $code == '401' || $code == '403' ) {

if ( $this->db != null ) {

$result = $this->mysqli->query( 'SELECT DATABASE()' );
list($db, ) = $result->fetch_row();

if ( $db != $this->db ) $this->mysqli->select_db( $this->db );
}

$this->mysqli->query( 'UPDATE google_accounts_tokens SET status=\'' . self::TOKEN_EXPIRED . '\' WHERE account=\'' . $this->shared_account . '\' AND service=\'' . self::SERVICE_DOCS . '\'' );

if ( $this->db != null && $db != $this->db )
$this->mysqli->select_db( $db );
}

return false;
}

//este método actualiza la tabla google_uploaded_objects con la información que tiene GoogleDocs
function downloadAndUpdate( $object_id, $etag, $ts_update, $local_file = null )
{

if ( $this->db != null ) {

$result = $this->mysqli->query( 'SELECT DATABASE()' );
list($db, ) = $result->fetch_row();

if ( $db != $this->db ) $this->mysqli->select_db( $this->db );
}

$result = $this->mysqli->query( 'SELECT export_link, local_mime_type, local_file, in_progress FROM google_uploaded_objects WHERE object_id=0x' . bin2hex( $object_id ) . ' FOR UPDATE' );

if ( $result->num_rows < 1 ) {

if ( $this->db != null && $db != $this->db )
$this->mysqli->select_db( $db );

return -1;
}

list($export_link, $local_mime_type, $local, $in_progress) = $result->fetch_row();


if ( $in_progress != '' ) {

if ( $this->db != null && $db != $this->db )
$this->mysqli->select_db( $db );

return 0;
}

$this->mysqli->query( 'UPDATE google_uploaded_objects SET in_progress=\'D\' WHERE object_id=0x' . bin2hex( $object_id ) );

if ( $local_file == null ) $local_file = $local;
else $local_file = realpath( $local_file );

$ext = self::$mime2export[ $local_mime_type ];

$code = '302';
$export_link.= '&exportFormat=' . $ext . '&format=' . $ext;


while ( $code == '302' ) {

list( $socket, $resource, $headers ) = $this->init( $export_link );

$req = 'GET ' . $resource . ' HTTP/1.1' . self::CRLF;
$req .= $headers . self::CRLF;

fwrite( $socket, $req );

$file_pointer = fopen( $local_file, 'w' ); //lo crea y lo trunca, perfect

list($code, $headers, $content) = self::getHTTPResponse( $socket,
 $file_pointer );

fclose( $file_pointer );

$export_link = $headers[ 'Location' ][ 0 ];
}



if ( $code == '200' ) {

$this->mysqli->query( 'UPDATE google_uploaded_objects SET in_progress=\'\', local_file=0x' . bin2hex( $local_file ) . ', local_file_size=' . filesize( $local_file ) . ', local_updated=FROM_UNIXTIME(' . $ts_update . '), etag=0x' . bin2hex( $etag ) . ', mime_type=0x' . bin2hex( $type ) . ' WHERE  object_id=0x' . bin2hex( $object_id ) );


if ( $this->db != null && $db != $this->db )
$this->mysqli->select_db( $db );

return 1;
}
else {

$this->mysqli->query( 'UPDATE google_uploaded_objects SET in_progress=\'\' WHERE  object_id=0x' . bin2hex( $object_id ) );

if ( $code == '401' || $code == '403' )
$this->mysqli->query( 'UPDATE google_accounts_tokens SET status=\'' . self::TOKEN_EXPIRED . '\' WHERE account=\'' . $this->shared_account . '\' AND service=\'' . self::SERVICE_DOCS . '\'' );


if ( $this->db != null && $db != $this->db )
$this->mysqli->select_db( $db );

return -1;
}
}

function uploadNewFile( $local_file, $title, $mime_type = null )
{

if ( empty( $mime_type ) ) {
$ext = end( explode( '.', $local_file ) );
$mime_type = self::$ext2mime[ $ext ];
}

$local_file = realpath( $local_file );

$data = $this->uploadFile( true, $this->resumable_create_media,
 $local_file, $mime_type, $title );

if ( $data == 0 ) return false;

else {

if ( $this->db != null ) {

$result = $this->mysqli->query( 'SELECT DATABASE()' );
list($db, ) = $result->fetch_row();

if ( $db != $this->db ) $this->mysqli->select_db( $this->db );
}

if ( $data != -1 ) {
//TENGO QUE OBTENER EL UPDATE DEL SERVIDOR!!!!!!!!!!!!!!!
list( $object_id, $local_file_size, $etag, $updated, $entry_link, $view_link, $edit_link, $export_link, $resumable_link, $remove_link, $access_control_link, $revisions_link ) = $data;

$this->mysqli->query( 'INSERT INTO google_uploaded_objects (object_id,local_file,local_file_size,local_updated,etag,entry_link,view_link,edit_link,export_link,resumable_edit_media,access_control_link,revisions_link) VALUES( 0x' . bin2hex( $object_id ) . ', 0x' . bin2hex( $local_file ) . ', ' . $local_file_size . ', FROM_UNIXTIME(' . $updated . '), 0x' . bin2hex( $etag ) . ', 0x' . bin2hex( $entry_link ) . ', 0x' . bin2hex( $view_link ) . ', 0x' . bin2hex( $edit_link ) . ', 0x' . bin2hex( $export_link ) . ', 0x' . bin2hex( $remove_link ) . ', 0x' . bin2hex( $resumable_link ) . ', 0x' . bin2hex( $access_control_link ) . ', 0x' . bin2hex( $revisions_link ) . ')' );

if ( $this->db != null && $db != $this->db )
$this->mysqli->select_db( $db );

return array( $object_id, $view_link, $edit_link, $export_link );
}
else {

$this->mysqli->query( 'UPDATE google_accounts_tokens SET status=\'' . self::TOKEN_EXPIRED . '\' WHERE account=\'' . $this->shared_account . '\' AND service=\'' . self::SERVICE_DOCS . '\'' );

if ( $this->db != null && $db != $this->db )
$this->mysqli->select_db( $db );

return false;
}
}
}

//POr defecto vamos a intentar convertirlos en un tipo que no sea file, preferiblemente document
function uploadAndUpdate( $object_id, $local_file = null, $convert = true )
{

if ( $this->db != null ) {

$result = $this->mysqli->query( 'SELECT DATABASE()' );
list($db, ) = $result->fetch_row();
$result->close();


if ( $db != $this->db ) $this->mysqli->select_db( $this->db );
}

$result = $this->mysqli->query( 'SELECT resumable_edit_media, local_mime_type, local_file FROM google_uploaded_objects WHERE object_id=0x' . bin2hex( $object_id ) );


if ( $result->num_rows < 1 ) {

if ( $this->db != null && $db != $this->db )
$this->mysqli->select_db( $db );

return false;
}

list( $resumable_link, $mime_type, $file ) = $result->fetch_row();

if ( empty( $local_file ) ) $local_file = $file;
else $local_file = realpath( $local_file );

$data = $this->uploadFile( false, $resumable_link, $local_file,
 $mime_type );
if ( $data == 0 ) return false;

else {

if ( $this->db != null ) {

$result = $this->mysqli->query( 'SELECT DATABASE()' );
list($db, ) = $result->fetch_row();

if ( $db != $this->db ) $this->mysqli->select_db( $this->db );
}

if ( $data != -1 ) {
list( $object_id, $local_file_size, $etag, $updated, $entry_link, $view_link, $edit_link, $export_link, $resumable_link, $remove_link, $access_control_link, $revisions_link ) = $data;

$this->mysqli->query( 'UPDATE google_uploaded_objects SET local_file=0x' . bin2hex( $local_file ) . ', local_file_size=' . $local_file_size . ', local_updated=FROM_UNIXTIME(' . $updated . '), etag=0x' . bin2hex( $etag ) . ', entry_link=0x' . bin2hex( $entry_link ) . ', view_link=0x' . bin2hex( $view_link ) . ', edit_link=0x' . bin2hex( $edit_link ) . ', export_link=0x' . bin2hex( $export_link ) . ', resumable_edit_media=0x' . bin2hex( $resumable_link ) . ', remove_link=0x' . bin2hex( $remove_link ) . ', access_control_link=0x' . bin2hex( $access_control_link ) . ', revisions_link=0x' . bin2hex( $revisions_link ) . ' WHERE  object_id=0x' . bin2hex( $object_id ) );

if ( $this->db != null && $db != $this->db )
$this->mysqli->select_db( $db );

return true;
}
else {
$this->mysqli->query( 'UPDATE google_accounts_tokens SET status=\'' . self::TOKEN_EXPIRED . '\' WHERE account=\'' . $this->shared_account . '\' AND service=\'' . self::SERVICE_DOCS . '\'' );

if ( $this->db != null && $db != $this->db )
$this->mysqli->select_db( $db );

return false;
}
}
}

//deberá devolver un array de datos cuando finalize, 0 cuando este subiendo y -1 ante cualquier error
//OJO EL LOCAL FILE DEBERÍA TENER UNA REVISION DIFERENTE CON LO CUAL UN NOMBRE DIFRENTE
function initOIDResumable( $object_id, $local_file )
{ //aqui podremos actualizar el nombre del localfile 
$result = $this->mysqli->query( 'SELECT resumable_edit_media, local_mime_type, in_progress, in_progress_amount, in_progress_link FROM google_uploaded_objects WHERE lcoal_file=0x' . bin2hex( $local_file ) );


if ( $result->num_rows > 0 ) {

//y el archivo tiene un resumable_edit_media --> no es nuevo y tiene object_id
list( $resumable_edit_media, $local_mime_type, $in_progess, $in_progress_amount, $in_progress_link ) = $result;

if ( $in_progress == 'D' || $in_progress == '' && $bytes_sent > 0 ) { //tengo que empezar desde el principio la subida del archivo
if ( $this->db != null && $db != $this->db )
$this->mysqli->select_db( $db );

return -1;
}

if ( $bytes_sent != $in_progress_amount ) {

//esto es un verdadero problema ya que no sabemos que ha pasado y deberíamos reiniciar la subida en curso
$this->mysqli->query( 'UPDATE google_uploaded_objects SET in_progress=\'\', in_progress_amount=0, in_progress_link=\'\'  WHERE local_file=0x' . bin2hex( $local_file ) );

if ( $this->db != null && $db != $this->db )
$this->mysqli->select_db( $db );

return -1;
}

//SIEMPRE TIENEN QUE COINCIDIR bytes_sent CON in_progress_amount
}
}

//tendría que crear una function que inicie transferencias

function initTransfer( $object_id = null )
{
//si tenemos 
//esto debería devolver 
}

function finishTransfer( $local_file = null )
{

}

function uploadChunk( $upload_link, &$next_chunk, $chunk_size, $bytes_sent,
 $file_size, $mime_type )
{

//$next_bytes_sent = $bytes_sent + $this_chuck;

list( $socket, $resource, $headers ) = $this->init( $upload_link );

$req = 'POST ' . $resource . ' HTTP/1.1' . self::CRLF;
$req .= 'Content-Length: ' . $chunk_size . self::CRLF;
$req .= 'Content-Type: ' . $mime_type . self::CRLF;
$req .= 'Content-Range: ' . $bytes_sent . '-' . ( $bytes_sent + $chunk_size ) . '/' . $file_size . self::CRLF;
$req .= $headers . self::CRLF;

$req .= $next_chunk;
//FALTA EL FICHERO LERDO!

list( $code, $headers, $content ) = self::getHTTPResponse( $socket );
//unset($content);

if ( $code == '201' ) {

$bytes_sent = $file_size;

$xml = simplexml_load_string( $content );

//$etag=$headers['Etag'][0];
$etag = ( string ) $xml[ 'etag' ];

$export_link = ( string ) $xml->content[ 'src' ];


foreach ( $xml->category as $c ) {
$scheme = ( string ) $c[ 'scheme' ];

if ( $scheme == 'http://schemas.google.com/g/2005#kind' ) {
$type = ( string ) $c[ 'label' ];
break;
}
}

$updated = strtotime( ( string ) $e->updated );
$object_id = ( string ) $xml->resourceId; //con el tipo delante

foreach ( $xml->link as $l ) {

$rel = ( string ) $l[ 'rel' ];

if ( $rel == 'self' ) $entry_link = ( string ) $l[ 'href' ];

else if ( $rel == 'alternate' )
$view_link = ( string ) $l[ 'href' ];

else if ( $rel == 'edit' ) $delete_link = ( string ) $l[ 'href' ];

else if ( $rel == 'http://schemas.google.com/docs/2007#embed' )
$edit_link = ( string ) $l[ 'href' ];

else if ( $rel == 'http://schemas.google.com/g/2005#resumable-edit-media' )
$resumable_link = ( string ) $l[ 'href' ];
}

foreach ( $xml->feedLink as $f ) {
$rel = ( string ) $f[ 'rel' ];

if ( $rel == 'http://schemas.google.com/acl/2007#accessControlList' )
$access_control = ( string ) $f[ 'href' ];

else if ( $rel == 'http://schemas.google.com/docs/2007/revisions' )
$revisions_link = ( string ) $f[ 'href' ];
}

return array( $code, $object_id, $file_size, $etag, $updated, $entry_link,
 $view_link, $edit_link, $export_link, $resumable_link, $delete_link,
 $access_control, $revisions_link );
//tengo que actualizar el objeto en la base de datos
}
else if ( $code == '308' ) {

list( $bytes_begin, $bytes_sent ) = explode( '-',
 $headers[ 'Range' ][ 0 ] );

return array( $code, $bytes_begin, $bytes_sent, $headers[ 'Location' ][ 0 ] );
} else if ( $code == '403' || $code == '401' ) return -1;
else return 0;
}

//dependiendo de si es nuevo o vamos a actualizar uno tendrá que se POST o PUT //si no me dan el mime type tengo que obtenerlo de la extension del archivo
//en caso de no tenerla sera de tipo file y mime type
private function uploadFile( $new, $upload_link, $local_file, $mime_type,
 $title = null )
{

$type = self::$mime2type[ $mime_type ];

$atom_xml = '';

if (!empty( $title ) ) {
if ( $new ) {

$atom_xml = '<?xml version="1.0" encoding="UTF-8"?>
    <entry xmlns="http://www.w3.org/2005/Atom" xmlns:docs="http://schemas.google.com/docs/2007">
    <!-- Replace the following line appropriately to create another type of resource. -->
    <category scheme="http://schemas.google.com/g/2005#kind"
        term="http://schemas.google.com/docs/2007#' . $type . '"/>
    <title>' . htmlspecialchars( $title ) . '</title>
    </entry>';
} else {

$atom_xml = '<?xml version="1.0" encoding="UTF-8"?>
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:docs="http://schemas.google.com/docs/2007">
  <title>' . htmlspecialchars( $title ) . '</title>
</entry>';
}
}

$file_size = filesize( $local_file );
$file_handler = fopen( $local_file, "rb" );

list( $socket, $resource, $headers ) = $this->init( $upload_link );

$req = ($new ? 'POST ' : 'PUT ') . $resource . ' HTTP/1.1' . self::CRLF;


if (!$new ) $req .= 'If-Match: *' . self::CRLF;

if (!empty( $title ) ) {
$req .= 'Content-Type: application/atom+xml';
$req .= 'Content-Length: ' . strlen( $atom_xml ) . self::CRLF;
}
else $req .= 'Content-Length: 0' . self::CRLF;

$req .= 'X-Upload-Content-Length: ' . $file_size . self::CRLF;
$req .= 'X-Upload-Content-Type: ' . $mime_type . self::CRLF;
$req .= $headers . self::CRLF;

fwrite( $socket, $req );

list( $code, $headers, $content ) = self::getHTTPResponse( $socket );


if ( $code == '403' || $code == '401' ) return -1;


$upload_link = $headers[ 'Location' ][ 0 ];

$chunk_size = 512 * 1024;
$bytes_sent = 0;

$next_bytes_sent = 0;

while ( $bytes_sent < $file_size ) {

$this_chuck = ( $file_size - $bytes_sent );

if ( $this_chuck > $chunk_size ) $this_chuck = $chunk_size;

$next_bytes_sent = $bytes_sent + $this_chuck;

$result = $this->uploadChunk( $upload_link,
 fread( $file_handler, $this_chuck ), $this_chuck,
 $bytes_sent, $file_size, $mime_type );

if ( $result == 0 || $result == -1 || is_array( $result ) && $result[ 0 ] == '201' ) {

fclose( $file_handler );

return $result;
} else if ( is_array( $result ) && $result[ 0 ] == '308' ) {

list( $code, $bytes_begin, $bytes_sent, $upload_link ) = $result;

if ( $next_bytes_sent != $bytes_sent )
fseek( $file_handler, $bytes_sent );
}
else {
fclose( $file_handler );

return false;
}
}
}

function share( $object_id, $perm, $who )
{

if ( $perm != self::PERMISSION_READER && $perm != self::PERMISSION_WRITER )
return false;

if ( $this->db != null ) {

$result = $this->mysqli->query( 'SELECT DATABASE()' );
list($db, ) = $result->fetch_row();

if ( $db != $this->db ) $this->mysqli->select_db( $this->db );
}

$result = $this->mysqli->query( 'SELECT type, edit_link FROM google_authorized_accounts WHERE owner_account=\'' . $this->shared_account . '\' AND authorized_account=\'' . $who . '\' AND object_id=\'' . $object_id . '\' AND service=\'' . self::SERVICE_DOCS . '\'' );

$newpermision = true;
if ( $result->num_rows > 0 ) {

$newpermision = false;
list( $old_perm, $edit_link ) = $result->fetch_row();

$result->free();

if ( $old_perm == $perm ) {


if ( $this->db != null && $db != $this->db )
$this->mysqli->select_db( $db );

return true;
}
}

$result = $this->mysqli->query( 'SELECT access_control_link FROM google_uploaded_objects WHERE object_id=0x' . bin2hex( $object_id ) );

if ( $result->num_rows < 1 ) {
if ( $this->db != null && $db != $this->db )
$this->mysqli->select_db( $db );

return false;
}

list( $access_control_link, ) = $result->fetch_row();

$socket = null;
$headers = null;

if ( $newpermision ) {

list( $socket, $resource, $headers ) = $this->init( $access_control_link );

$req = 'POST ' . $resource . ' HTTP/1.1' . self::CRLF;
} else {

list( $socket, $resource, $headers ) = $this->init( $edit_link );

$req = 'PUT ' . $resource . ' HTTP/1.1' . self::CRLF;
//$req = 'POST '.$resource .' HTTP/1.1'. self::CRLF;
//$req .= 'X-HTTP-Method-Override: PUT'.self::CRLF;
}

$atom_xml = '<?xml version="1.0" encoding="UTF-8"?>';
$atom_xml .= '<entry xmlns="http://www.w3.org/2005/Atom" xmlns:gAcl="http://schemas.google.com/acl/2007">';
$atom_xml .= '<category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/acl/2007#accessRule"/>';

if ( $newpermision ) $atom_xml .= '<gAcl:role value="' . $perm . '"/>';
else {
$atom_xml .= '<gAcl:role value="' . $old_perm . '"/>';
$atom_xml .= '<gAcl:additonalRole value="' . $perm . '" />';
}

$atom_xml .= '<gAcl:scope type="user" value="' . htmlspecialchars( $who ) . '" />';
$atom_xml .= '</entry>';

$req .= 'Content-Type: application/atom+xml' . self::CRLF;
$req .= 'Content-Length: ' . strlen( $atom_xml ) . self::CRLF;
$req .= $headers . self::CRLF;

fwrite( $socket, $req );

list( $code, $headers, $content ) = self::getHTTPResponse( $socket );


if ( $code == '403' || $code == '401' ) {

$this->mysqli->query( 'UPDATE google_accounts_tokens SET status=\'' . self::TOKEN_EXPIRED . '\' WHERE account=\'' . $this->shared_account . '\' AND service=\'' . self::SERVICE_DOCS . '\'' );


if ( $this->db != null && $db != $this->db )
$this->mysqli->select_db( $db );

return false;
}
else if ( $code == '200' || $code == '201' ) {

if ( $newpermision )
$this->mysqli->query( 'INSERT INTO google_authorized_accounts (owner_account,authorized_account,object_id,type,service) VALUES (\'' . $this->shared_account . '\',\'' . $who . '\',\'' . $docid . '\',\'' . $perm . '\',\'' . self::SERVICE_DOCS . '\')' );
else
$this->mysqli->query( 'UPDATE google_authorized_accounts SET type=\'' . $perm . '\' WHERE owner_account=\'' . $this->shared_account . '\' AND authorized_account=\'' . $who . '\' AND object_id=\'' . $docid . '\' AND service=\'' . self::SERVICE_DOCS . '\'' );


if ( $this->db != null && $db != $this->db )
$this->mysqli->select_db( $db );

return true;
}
}

//si cambio los permisos de proyecto o milestone debo elimiar todos los permisos
function unShare( $objects_ids )
{
if ( empty( $objects_ids ) ) return false;


$this->init( self::SERVICE_DOCS );

if ( $this->db != null ) {

$result = $this->mysqli->query( 'SELECT DATABASE()' );
list($db, ) = $result->fetch_row();

if ( $db != $this->db ) $this->mysqli->select_db( $this->db );
}

//SOLO LOS DOCSIDS QUE ESTEN
$hex_objs_ids = array( );
foreach ( $objects_ids as $o ) $hex_objs_ids[ ] = bin2hex( $o );

$access_link = array( );

$result = $this->mysqli->query( 'SELECT object_id, access_control_link FROM google_uploaded_objects WHERE object_id IN (0x' . implode( ',0x',
 $hex_objs_ids ) . ')' );

if ( $result->num_rows < 1 ) {

if ( $this->db != null && $db != $this->db )
$this->mysqli->select_db( $db );

return false;
}

while ( $row = $result->fetch_row() )
$access_link[ $row[ 0 ] ] = $row[ 1 ];

$result->free();

$result = $this->mysqli->query( 'SELECT authorized_account, type, object_id FROM google_authorized_accounts WHERE owner_account=\'' . $this->shared_account . '\' AND object_id IN (0x' . implode( ',0x',
 $hex_objs_ids ) . ') ORDER BY object_id' );

$atom_xml = '';
$socket = null;
$accs_link = '';
$strlen_accs_link = 0;

if ( $result->num_rows > 0 ) {

$current_object_id = null;



while ( $row = $result->fetch_row() ) {

list($account, $type, $object_id) = $row;


if ( $current_object_id != $object_id ) {


if ( $current_object_id != null ) {

$atom_xml.='</feed>';

$req = 'Content-Length: ' . strlen( $atom_xml ) . self::CRLF . self::CRLF . $atom_xml;

fwrite( $socket, $req );

list($code, $headers, $content) = self::getHTTPResponse( $socket );

//si esta
if ( $code == '403' || $code == '401' ) {

$this->mysqli->query( 'UPDATE google_accounts_tokens SET status=\'' . self::TOKEN_EXPIRED . '\' WHERE account=\'' . $this->shared_account . '\' AND service=\'' . self::SERVICE_DOCS . '\'' );

if ( $this->db != null && $db != $this->db )
$this->mysqli->select_db( $db );

return false;
}
else {

$xml = simplexml_load_string( $content );

$delete = array( );
if ( isset( $xml->entry[ 1 ] ) ) {
foreach ( $xml->entry as $e )
if ( (( string ) $e->operation[ 'type' ]) == 'delete' && (( string ) $e->status[ 'reason' ]) == 'Success' )
$delete[ ] = substr( $e->id,
 $strlen_accs_link );
}
else if ( isset( $xml->entry ) )
if ( (( string ) $xml->entry->operation[ 'type' ]) == 'delete' && (( string ) $xml->entry->status[ 'reason' ]) == 'Success' )
$delete[ ] = substr( $xml->entry->id,
 $strlen_accs_link );

unset( $xml );

//$this->mysqli->query('DELETE FROM google_authorized_accounts WHERE object_id=\''.$current_object_id.'\' AND authorized_account IN (\''.implode('\',\'',$delete).'\') AND service=\''.self::SERVICE_DOCS.'\'');
$this->mysqli->query( 'DELETE FROM google_authorized_accounts WHERE object_id=\'' . $current_object_id . '\' AND authorized_account IN (\'' . implode( '\',\'',
 $delete ) . '\')' );
}
}

$current_object_id = $object_id;
$accs_link = $access_link[ $current_object_id ];
$strlen_accs_link = strlen( $accs_link . '/user:' );

list( $socket, $resource, $headers ) = $this->init( $accs_link );

$req = 'POST ' . $resource . '/batch HTTP/1.1' . self::CRLF;
$req .= $headers;
$req .= 'Content-Type: application/atom+xml' . self::CRLF;

fwrite( $socket, $req );

$atom_xml = '<feed xmlns="http://www.w3.org/2005/Atom" xmlns:gAcl="http://schemas.google.com/acl/2007"';
$atom_xml .=' xmlns:batch="http://schemas.google.com/gdata/batch">';
$atom_xml .='<category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/acl/2007#accessRule"/>';
}

$atom_xml.='<entry>';
$atom_xml.='<id>' . htmlspecialchars( $accs_link ) . '/user:' . htmlspecialchars( $account ) . '</id>';
$atom_xml.='<batch:operation type="delete" />';
$atom_xml.='<gAcl:role value="' . $type . '" />';
$atom_xml.='<gAcl:scope type="user" value="' . htmlspecialchars( $account ) . '" />';
$atom_xml.='</entry>';
}

$result->free();

$atom_xml.='</feed>';

$req = 'Content-Length: ' . strlen( $atom_xml ) . self::CRLF . self::CRLF . $atom_xml;

fwrite( $socket, $req );

list($code, $headers, $content) = self::getHTTPResponse( $socket );

//si esta
if ( $code == '403' || $code == '401' ) {

$this->mysqli->query( 'UPDATE google_accounts_tokens SET status=\'' . self::TOKEN_EXPIRED . '\' WHERE account=\'' . $this->shared_account . '\' AND service=\'' . self::SERVICE_DOCS . '\'' );

if ( $this->db != null && $db != $this->db )
$this->mysqli->select_db( $db );

return false;
}
else {

$xml = simplexml_load_string( $content );

$delete = array( );
if ( isset( $xml->entry[ 1 ] ) ) {
foreach ( $xml->entry as $e )
if ( (( string ) $e->operation[ 'type' ]) == 'delete' && (( string ) $e->status[ 'reason' ]) == 'Success' )
$delete[ ] = substr( $e->id, $strlen_accs_link );
}
else if ( isset( $xml->entry ) )
if ( (( string ) $xml->entry->operation[ 'type' ]) == 'delete' && (( string ) $xml->entry->status[ 'reason' ]) == 'Success' )
$delete[ ] = substr( $xml->entry->id,
 $strlen_accs_link );

unset( $xml );

$this->mysqli->query( 'DELETE FROM google_authorized_accounts WHERE object_id=\'' . $current_object_id . '\' AND authorized_account IN (\'' . implode( '\',\'',
 $delete ) . '\')' );
//$this->mysqli->query('DELETE FROM google_authorized_accounts WHERE object_id=\''.$current_object_id.'\' AND authorized_account IN (\''.implode('\',\'',$delete).'\') AND service=\''.self::SERVICE_DOCS.'\'');
}
}


if ( $this->db != null && $db != $this->db )
$this->mysqli->select_db( $db );

return true;
}

/*
  private function uploadDoc($input_file, $title, $mime=null){

  $this->init(self::SERVICE_DOCS);


  $fh = fopen($input_file, 'r+'); //Bloqueo el archivo para evitar accesos recursivos

  if(!$fh)
  return false;

  $result=$this->mysqli->query('SELECT uploaded, object_id, mime_type, title FROM google_uploaded_objects WHERE local_file=\''.$input_file.'\'');

  $update=true;
  if($result->num_rows>0){

  list($uploaded,$docid,$mime,$title) = $result->fetch_row();

  if($uploaded=='Y'){

  $this->mysqli->query('UPDATE google_uploaded_objects SET access=NOW() WHERE local_file=\''.$input_file.'\'');

  fclose($fh);

  return $docid;
  }


  $result->free();
  }
  else{

  if(empty($mime)){

  $ext=strtolower(substr($input_file,strrpos($input_file,'.')+1));

  if(isset(self::$ext2mime[$ext]))
  $mime=self::$ext2mime[$ext];

  else
  return false;
  }
  else if(!isset(self::$mime2export[$mime]))
  return false;

  $update=false;
  }



  $req = 'POST /feeds/documents/private/full HTTP/1.1' . self::CRLF;
  $req .= 'GData-Version: 2.0'. self::CRLF;
  $req .= $this->auth_token[self::SERVICE_DOCS]. self::CRLF;
  $req .= 'Host: docs.google.com'. self::CRLF;
  $req .= 'Connection: Keep-Alive'. self::CRLF;
  // $req .= 'Content-Encoding: gzip'.self::CRLF;
  $req .= 'Content-Type: Multipart/Related; boundary="BOUNDARY_CONTENT"'. self::CRLF;

  fwrite($this->socket[self::SERVICE_DOCS], $req);


  $req = '--BOUNDARY_CONTENT'.self::CRLF;
  $req .= 'Content-Type: application/atom+xml'. self::CRLF. self::CRLF;

  $req .= '<?xml version="1.0" encoding="UTF-8"?>';
  $req .= '<atom:entry xmlns:atom="http://www.w3.org/2005/Atom" >';
  $req .= '<atom:category scheme="http://schemas.google.com/g/2005#kind"';
  $req .= ' term="http://schemas.google.com/docs/2007#document" label="document"/>';
  $req .= '<atom:title>'.htmlspecialchars($title).'</atom:title>';
  $req .= '</atom:entry>'.self::CRLF.self::CRLF;
  $req .= '--BOUNDARY_CONTENT'.self::CRLF;


  //
  //
  //$req .= 'Content-Transfer-Encoding: base64'. self::CRLF;
  $req .= 'Content-Transfer-Encoding: binary'. self::CRLF;
  $req .= 'Content-Type: ' . $mime . self::CRLF. self::CRLF;

  //        $req .= 'Content-Type: application/octet-stream' . self::CRLF. self::CRLF;




  while(!feof($fh))
  $req.=fread($fh, 3000);


  $req .= self::CRLF.self::CRLF.'--BOUNDARY_CONTENT--';


  fwrite($this->socket[self::SERVICE_DOCS], 'Content-Length: '.strlen($req).self::CRLF. self::CRLF);

  fwrite($this->socket[self::SERVICE_DOCS], $req);


  list($code,$headers,$content)=self::getHTTPResponse($this->socket[self::SERVICE_DOCS]);

  $id=false;

  if($code=='201'){
  $xml=simplexml_load_string($content);


  //$id=substr($xml->resourceId,strlen('document:'));
  $id=substr($xml->id,strrpos($xml->id,'%3A')+3);

  unset($xml);

  if($update)
  $this->mysqli->query('UPDATE google_uploaded_objects SET object_id=\''.$id.'\', access=NOW(), local_updated=NOW() WHERE local_file=\''.$input_file.'\'');

  else
  $this->mysqli->query('INSERT INTO google_uploaded_objects (local_file,object_id,mime_type,title,service) VALUES (\''.$input_file.'\',\''.$id.'\',\''.$mime.'\',0x'.bin2hex($title).',\''.self::SERVICE_DOCS.'\')');

  //LA SIGUIENTE QUERY DEBERÍA ESTAR HECHA EN EL MOMENTO QUE SE ELIMINE EL DOCUMENTO DE GOOGLE DOCS
  // if(!empty($id))
  //   $this->mysqli->query('DELETE FROM google_authorized_accounts WHERE object_id=\''.$id.'\' AND service=\''.self::SERVICE_DOCS.'\'');
  }


  fclose($fh); //libero recurso

  return $id;
  }

 */

function removeFile( $docid )
{
//Antes tengo que comprobar si está actualizado y bajarlo si es necesario

if ( $this->db != null ) {

$result = $this->mysqli->query( 'SELECT DATABASE()' );
list($db, ) = $result->fetch_row();

if ( $db != $this->db ) $this->mysqli->select_db( $this->db );
}

$result = $this->mysqli->query( 'SELECT remove_link FROM google_uploaded_objects WHERE object_id=0x' . bin2hex( $docid ) );

list($remove_link, ) = $result->fetch_row();

list( $socket, $resource, $headers ) = $this->init( $remove_link . '?delete=true' );

$req = 'DELETE ' . $resource . ' HTTP/1.1' . self::CRLF;
$req .= 'If-Match: *' . self::CRLF;
$req .= $headers . self::CRLF;

fwrite( $socket, $req );

list($code, $headers, $content) = self::getHTTPResponse( $socket );

if ( $code == '200' ) {

//marcamos el objeto como no subido
$this->mysqli->query( 'DELETE FROM google_uploaded_objects WHERE object_id=0x' . bin2hex( $docid ) );

$this->mysqli->query( 'DELETE FROM google_authorized_accounts WHERE object_id=0x' . bin2hex( $docid ) );

if ( $this->db != null && $db != $this->db )
$this->mysqli->select_db( $db );

return true;
}

if ( $this->db != null && $db != $this->db )
$this->mysqli->select_db( $db );

return false;
}

function dailyRevision()
{

//puedo primero obtener los updated locales

if ( $this->db != null ) {

$result = $this->mysqli->query( 'SELECT DATABASE()' );
list($db, ) = $result->fetch_row();

if ( $db != $this->db ) $this->mysqli->select_db( $this->db );
}

$updated_time = array( );
$result = $this->mysqli->query( 'SELECT object_id, UNIX_TIMESTAMP(local_updated) FROM google_uploaded_objects' );
while ( $row = $result->fetch_row() )
$updated_time[ $row[ 0 ] ] = ( int ) $row[ 1 ];

$result->free();

list( $socket, $resource, $headers ) = $this->init( $this->post_link );


$req = 'GET ' . $resource . ' HTTP/1.1' . self::CRLF;
$req .= $headers . self::CRLF;

fwrite( $socket, $req );

list($code, $headers, $content) = self::getHTTPResponse( $socket );

$xml = simplexml_load_string( $content );


$google_updated = array( );

//puedo mantener al margen los archivos que tienen una versión más reciente el google y centrarme en los otros
//esos otros es seguro eliminarlos

if ( isset( $xml->entry[ 1 ] ) ) {

foreach ( $xml->entry as $e ) {
$id = ( string ) $e->resourceId;
$updated = strtotime( ( string ) $e->updated );

if ( isset( $updated_time[ $id ] ) && $updated <= $updated_time[ $id ] )
$google_updated[ ] = $id;
}
}
else if ( isset( $xml->entry ) ) {
$id = ( string ) $e->resourceId;
$updated = strtotime( ( string ) $xml->entry->updated );

if ( isset( $updated_time[ $id ] ) && $updated <= $updated_time[ $id ] )
$google_updated[ ] = $id;
}

unset( $xml );


list( $socket, $resource, $headers ) = $this->init( 'https://doc.google.com/feeds/metadata/default' );

$req = 'GET ' . $resource . ' HTTP/1.1' . self::CRLF;
$req .= $headers . self::CRLF;

fwrite( $socket, $req );

list($code, $headers, $content) = self::getHTTPResponse( $socket );

$pos_ini = strpos( $content, '<gd:quotaBytesTotal>' ) + strlen( '<gd:quotaBytesTotal>' );
$pos_fin = strrpos( $content, '</gd:quotaBytesTotal>' );
$quotaBytesTotal = ( int ) substr( $content, $pos_ini, $pos_fin - $pos_ini );

$pos_ini = strpos( $content, '<gd:quotaBytesUsed>' ) + strlen( '<gd:quotaBytesUsed>' );
$pos_fin = strrpos( $content, '</gd:quotaBytesUsed>' );
$quotaBytesUsed = ( int ) substr( $content, $pos_ini, $pos_fin - $pos_ini );

unset( $content );

$quota = 1 - $quotaBytesUsed / $quotaBytesTotal;
$toEmpty = $quotaBytesUsed - ( 1 - 0.2 ) * $quotaBytesTotal;

if ( $quota < 0.2 ) {

$result = $this->mysqli->query( 'SELECT object_id, local_file_size FROM google_uploaded_objects WHERE object_id NOT IN (\'' . implode( '\',\'',
 $google_updated ) . '\') ORDER BY access ASC' );

$emptySize = 0;
while ( $emptySize < $toEmpty && ( $row = $result->fetch_row() ) )
if ( $this->removeFile( $row[ 0 ] ) );
$emptySize += ( int ) $row[ 1 ];

$result->free();
}


if ( $db != $this->db ) $this->mysqli->select_db( $this->db );

return $google_updated;
}

function __sleep()
{
foreach ( $this->socket as $s )
if (!empty( $s ) ) fclose( $s );
}

//esto tiene que devolver un arrray con codigo, cabecera y contenido
private static function getHTTPResponse( $socket, $fp = null )
{
$linea = self::CRLF;
while ( $linea == self::CRLF )
$linea = fgets( $socket );

$responseCode = ( int ) substr( $linea, 9, 3 );
//  $this->responseVersion= substr($linea,6,3);

$responseHeaders = array( );

$linea = substr( fgets( $socket ), 0, -2 );



while (!feof( $socket ) && $linea != '' ) {

$dos_puntos = strpos( $linea, ':' );
$name = ucwords( trim( substr( $linea, 0, $dos_puntos ) ) );
$value = trim( substr( $linea, $dos_puntos + 1 ) );


if (!isset( $responseHeaders[ $name ] ) )
$responseHeaders[ $name ] = array( );

$responseHeaders[ $name ][ ] = $value;


$linea = substr( fgets( $socket ), 0, -2 );
}

$responseContent = '';
if ( isset( $responseHeaders[ 'Transfer-Encoding' ] ) && $responseHeaders[ 'Transfer-Encoding' ][ 0 ] == 'chunked' ) {

$tam = hexdec( substr( fgets( $socket ), 0, -2 ) );


while (!feof( $socket ) && $tam > 0 ) {

while (!feof( $socket ) && $tam > 0 ) {
$linea = fread( $socket, $tam );

if (!empty( $fp ) ) fwrite( $fp, $linea );
else $responseContent.=$linea;

$tam-=strlen( $linea );
}

$linea = fgets( $socket );

while ( $linea == self::CRLF )
$linea = fgets( $socket );

$tam = hexdec( substr( $linea, 0, -2 ) );
}
}
else if ( isset( $responseHeaders[ 'Content-Length' ] ) ) {

$tam = ( int ) $responseHeaders[ 'Content-Length' ][ 0 ];

while (!feof( $socket ) && $tam > 0 ) {
$linea = fread( $socket, $tam );

if (!empty( $fp ) ) fwrite( $fp, $linea );
else $responseContent.=$linea;

$tam-=strlen( $linea );
}
}
else {
while (!feof( $socket ) ) {
$linea = fread( $socket, 1024 );

if (!empty( $fp ) ) fwrite( $fp, $linea );
else $responseContent.=$linea;
}
}

return array( $responseCode, $responseHeaders, $responseContent );
}

static function initializeInnoDB( MySQLi $mysqli, $db = null )
{

if ( $db != null ) {
$result = $mysqli->query( "SELECT DATABASE()" );
list($olddb, ) = $result->fetch_row();

if ( $db != $olddb ) $mysqli->select_db( $db );
}


$query = 'CREATE TABLE `google_accounts` (';
$query.='`account` varchar(255) character set ascii collate ascii_bin NOT NULL,';
$query.='`password` varbinary(255) character set ascii collate ascii_bin NOT NULL,';
$query.='`resumable_create_media` VARCHAR( 255 ) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL ,';
$query.='`alt_post` VARCHAR( 255 ) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL ,';
$query.='`feed` VARCHAR( 255 ) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL ,';
$query.='`post` VARCHAR( 255 ) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL ,';
$query.='`batch` VARCHAR( 255 ) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,';
$query.='PRIMARY KEY  (`account`)';
$query.=') ENGINE=InnoDB DEFAULT CHARSET=ascii';

$this->mylink->query( $query );

$query = 'CREATE TABLE `google_accounts_tokens` (';
$query.='`account` varchar(255) character set ascii collate ascii_bin NOT NULL,';
$query.='`service` enum(\'' . implode( '\',\'',
 array_keys( self::$scopes ) ) . '\') character set ascii collate ascii_bin NOT NULL,';
$query.='`token` varchar(266) character set ascii collate ascii_bin NOT NULL,';
$query.='`status` enum(\'active\',\'expired\') character set ascii NOT NULL default \'active\',';
$query.='`created` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,';
$query.='`expires` timestamp NOT NULL default \'0000-00-00 00:00:00\',';
$query.='PRIMARY KEY  (`account`,`service`)';
$query.=') ENGINE=InnoDB DEFAULT CHARSET=ascii';

$this->mylink->query( $query );

$query = 'CREATE TABLE `google_authorized_accounts` (';
$query.='`owner_account` varchar(255) character set ascii collate ascii_bin NOT NULL,';
$query.='`authorized_account` varchar(300) character set ascii collate ascii_bin NOT NULL,';
$query.='`service` enum(\'' . implode( '\',\'',
 array_keys( self::$scopes ) ) . '\') character set ascii NOT NULL,';
$query.='`object_id` varchar(50) character set ascii collate ascii_bin NOT NULL,';
$query.=' `type` enum(\'writer\',\'reader\',\'owner\') character set ascii NOT NULL,';
$query.='`edit_link` varchar(255) character set ascii NOT NULL,';
$query.='`created` timestamp NOT NULL default CURRENT_TIMESTAMP';
$query.=') ENGINE=InnoDB DEFAULT CHARSET=latin1';

$this->mylink->query( $query );

$query = 'CREATE TABLE `google_uploaded_objects` (';
$query.='`local_file` varchar(255) character set ascii collate ascii_bin NOT NULL,';
$query.='`local_file_size` INT(15) NOT NULL,';
$query.='`local_updated` timestamp NOT NULL default \'0000-00-00 00:00:00\',';
$query.='`local_mime_type` varchar(75) character set ascii NOT NULL,';

$query.=' `type` enum(\'document\', \'drawing\', \'file\', \'folder\', \'pdf\', \'presentation\', \'spreadsheet\') character set ascii NOT NULL,';
$query.='`title` varchar(100) character set utf8 NOT NULL,';
$query.='`created` timestamp NOT NULL default CURRENT_TIMESTAMP,';
$query.='`access` timestamp NOT NULL default \'0000-00-00 00:00:00\',';
$query.='`object_id` varchar(100) character set ascii collate ascii_bin NOT NULL,';
$query.='`entry_link` varchar(255) character set ascii NOT NULL,';
$query.='`view_link` varchar(255) character set ascii NOT NULL,';
$query.='`edit_link` varchar(255) character set ascii NOT NULL,';
$query.='`export_link` varchar(255) character set ascii NOT NULL,';
$query.='`remove_link` varchar(255) character set ascii NOT NULL,';
$query.='`resumable_edit_media` varchar(255) character set ascii NOT NULL,';
$query.='`access_control_link` varchar(255) character set ascii NOT NULL,';
$query.='`revisions_link` varchar(255) character set ascii NOT NULL,';
$query.='PRIMARY KEY  (`local_file`),';
$query.='KEY `object_id` (`object_id`,`service`)';
$query.=') ENGINE=InnoDB DEFAULT CHARSET=latin1';

$this->mylink->query( $query );

if ( $db != null && $db != $olddb ) $mysqli->select_db( $olddb );
}

}
?>
